﻿/*----------------------------------------------------------------
//  <copyright file="JwtBearerHostedService.cs" company="MicroCloud@151504200868">
//      Copyright © 2020-2024 MicroCloud Corporation, All rights reserved.
//  </copyright>
//  <site>https://gitee.com/chenmm123/microclouds</site>
//  <last-editor>cmm</last-editor>
//  <last-date>2023-12-06 09:30</last-date>
//----------------------------------------------------------------*/

namespace MicroCloud.Authentication.JwtBearer
{
    #region "JwtBearer托管服务"
    /// <summary>
    /// JwtBearer托管服务
    /// </summary>
    /// <typeparam name="TUser">用户实体类型</typeparam>
    /// <typeparam name="TUserKey">用户实体主键类型</typeparam>
    /// <typeparam name="TUserToken">用户令牌类型</typeparam>
    /// <typeparam name="TUserTokenKey">用户令牌编号类型</typeparam>
    public class JwtBearerHostedService<TUser, TUserKey, TUserToken, TUserTokenKey> : IHostedService, IDisposable
        where TUser : UserBase<TUserKey>
        where TUserKey : struct, IEquatable<TUserKey>
        where TUserToken : UserTokenBase<TUserTokenKey, TUserKey>
        where TUserTokenKey : struct, IEquatable<TUserTokenKey>
    {
        #region "属性"
        #region "获取 服务提供者对象"
        /// <summary>
        /// 获取 服务提供者对象
        /// </summary>
        protected IServiceProvider ServiceProvider { get; }
        #endregion
        #region "获取 日志对象"
        /// <summary>
        /// 获取 日志对象
        /// </summary>
        protected ILogger Logger { get; }
        #endregion

        #region "获取 过期令牌移除定时器"
        /// <summary>
        /// 获取 过期令牌移除定时器
        /// </summary>
        protected Timer ExpiredTokenRemoveTimer { get; }
        #endregion

        #endregion

        #region "构造函数"
        #region "初始化一个 JwtBearer 托管服务的新实例"
        /// <summary>
        /// 初始化一个 JwtBearer 托管服务的新实例
        /// </summary>
        /// <param name="provider"></param>
        public JwtBearerHostedService(IServiceProvider provider)
        {
            ServiceProvider = provider;
            Logger = provider.GetLogger(GetType());

            ExpiredTokenRemoveTimer = new Timer(ExecuteExpiredTokenRemove, null, Timeout.Infinite, 0);
        }
        #endregion

        #endregion

        #region "方法"
        #region "异步启动"
        /// <summary>
        /// 异步启动
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task StartAsync(CancellationToken cancellationToken)
        {
            Logger.LogInformation("启动 JwtBearer 托管服务");

            SetExpiredTokenRemoveTask();

            return Task.CompletedTask;
        }
        #endregion
        #region "异步停止"
        /// <summary>
        /// 异步停止
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task StopAsync(CancellationToken cancellationToken)
        {
            Logger.LogInformation("停止 JwtBearer 托管服务");

            ExpiredTokenRemoveTimer?.Change(Timeout.Infinite, 0);

            return Task.CompletedTask;
        }
        #endregion
        #region "释放资源"
        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        /// <summary>
        /// 释放资源
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                ExpiredTokenRemoveTimer?.Dispose();
            }
        }
        #endregion

        #endregion

        #region "受保护的方法"
        #region "设置过期令牌移除任务"
        /// <summary>
        /// 设置过期令牌移除任务
        /// </summary>
        protected void SetExpiredTokenRemoveTask()
        {
            DateTime now = DateTime.Now;
            DateTime zeroOClock = DateTime.Today.AddHours(0.0); //凌晨00:00:00
            if (now > zeroOClock)
            {
                zeroOClock = zeroOClock.AddDays(1.0);
            }
            int msUntilFour = (int)((zeroOClock - now).TotalMilliseconds);

            ExpiredTokenRemoveTimer.Change(msUntilFour, Timeout.Infinite);
            //ExpiredTokenRemoveTimer.Change(5000, Timeout.Infinite);
        }
        #endregion
        #region "执行过期令牌移除"
        /// <summary>
        /// 执行过期令牌移除
        /// </summary>
        /// <param name="state"></param>
        protected void ExecuteExpiredTokenRemove(object state = null)
        {
            try
            {
                using var scope = ServiceProvider.CreateScope();
                IServiceProvider scopedProvider = scope.ServiceProvider;
                #region "UserManager处理模式-注释备查"
                //UserManager<TUser> userManager = scopedProvider.GetService<UserManager<TUser>>();
                //var userEntities = userManager.Users.ToList();
                //if (userEntities?.Count > 0)
                //{
                //    foreach (var userEntity in userEntities)
                //    {
                //        var refreshTokens = userEntity.GetRefreshTokensAsync(scopedProvider).Result;
                //        RefreshToken[] expiredTokens = refreshTokens.Where(m => m.ExpirationTimeUtc < DateTime.UtcNow).ToArray();
                //        if (expiredTokens?.Length > 0)
                //        {
                //            //删除过期的Token
                //            IUnitOfWork unitOfWork = scopedProvider.GetUnitOfWork(true);
                //            foreach (RefreshToken expiredToken in expiredTokens)
                //            {
                //                userManager.RemoveRefreshTokenAsync(userEntity, expiredToken.ClientId).Wait();
                //            }
                //            unitOfWork.Commit();
                //        }
                //    }
                //}
                #endregion
                var userTokenRepository = scopedProvider.GetService<IRepository<TUserToken, TUserTokenKey>>();
                var userTokens = userTokenRepository.QueryAsNoTracking().ToList();
                if (userTokens?.Count > 0)
                {
                    var expiredTokens = userTokens.Select(s => new { s.Id, Value = s.Value.FromJsonString<RefreshToken>() }).Where(m => m.Value.ExpirationTimeUtc < DateTime.UtcNow).ToArray();
                    if (expiredTokens?.Length > 0)
                    {
                        //删除过期的Token
                        var expiredTokenIds = expiredTokens.Select(s => s.Id).ToList();
                        userTokenRepository.DeleteBatch(o => expiredTokenIds.Contains(o.Id));
                    }
                }

            }
            catch (System.Exception ex)
            {
                Logger.LogError(ex, "{message}", ex.Message);
            }
            finally
            {
                //再次设定
                SetExpiredTokenRemoveTask();
            }
        }
        #endregion

        #endregion

    }
    #endregion

}
